package com.plum.core;

import java.util.Map;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import com.plum.realm.PlumAbstractRealm;
import com.plum.session.PlumSession;

/**
 * Plum核心类
 * 
 * @author lijiahong
 * @CreateDate 2018年1月22日20:27:21
 *
 */
public class PlumSecurity extends PlumSession {
	protected static PlumAbstractRealm realm;

	public static void setRealm(PlumAbstractRealm myRealm) {
		realm = myRealm;
	}

	/**
	 * 获得plum.ini中的realm实例
	 * 
	 * @return
	 */
	public static PlumAbstractRealm getRealm() {
		return realm;
	}

	/**
	 * 提供plum登录方法,参数会传递给realm的loginAuth()并执行该方法,当realm.loginAuth()返回不为空则认为登录成功,
	 * 反之亦然
	 * 
	 * @param param
	 *            Map<String, Object>
	 * @return boolean
	 */
	public static boolean login(Map<String, Object> param) {
		Object obj = realm.loginAuth(param);
		if (obj != null) {
			setAttr(Plum.SESSION_USER_KEY, obj);
			setRoles();
			return true;
		}
		return false;
	}

	/**
	 * 获得当前登录用户
	 * 
	 * @return Object
	 */
	public static Object getUser() {
		return getAttr(Plum.SESSION_USER_KEY);
	}

	/***
	 * 获得当前登录用户的所有权限
	 * 
	 * @return
	 */
	@SuppressWarnings("unchecked")
	public static Set<String> getRoles() {
		return (Set<String>) getAttr(Plum.SESSION_ROLES_KEY);
	}

	/**
	 * 设置当前用户的权限
	 */
	public static void setRoles() {
		Set<String> roles = realm.setHaveRoles();
		setAttr(Plum.SESSION_ROLES_KEY, roles);
	}

	/**
	 * 判断当前登录用户是否具有该url权限
	 * 
	 * @param url
	 *            当前访问的url
	 * @return
	 */
	public static boolean hasRole(String url) {
		boolean ret = false;
		Set<String> hasRoles = getRoles();
		if (getUser() != null) {
			if (isRole(url, hasRoles))
				return true;
		} else {
			// 如果当前user为空代表通过过滤器的isAllow验证，代表是允许直接访问的，这里需要仔细想一下(比如session超时等情况的发生)
			ret = true;
		}
		return ret;
	}

	/**
	 * 注销当前用户
	 */
	public static void logout() {
		removeAttr(Plum.SESSION_USER_KEY);
		removeAttr(Plum.SESSION_ROLES_KEY);
	}

	/**
	 * 替换表达式
	 * 
	 * @param regix
	 * @return
	 */
	public static String replaceRegix(String regix) {
		if (regix.endsWith("/*")) {
			regix = regix.replace(regix.charAt(regix.length() - 2) + "", "/?");
		}
		regix = regix.replace("*", "\\S*");
		return regix;
	}

	/**
	 * 判断url是否允许未授权通过(roleMap)
	 * 
	 * @param url
	 * @return
	 */
	public static boolean isRole(String url, Set<String> hasRoles) {
		boolean ret = false;
		// 标记是否所有权限规则都不匹配
		boolean matchRegFlag = true;
		Object obj = getUser();
		if (obj != null) {
			Map<String, Set<String>> roleMap = Plum.getRoleMap();
			Map<String, String> urlCacheMap = Plum.getUrlRegixCacheMap();
			if (urlCacheMap.containsKey(url)) {
				String key = urlCacheMap.get(url);
				matchRegFlag = false;
				boolean[] b = checkRole(key, url, roleMap.get(key), hasRoles);
				if (b[0])
					ret = true;
			}
			for (Map.Entry<String, Set<String>> entry : roleMap.entrySet()) {
				String key = entry.getKey();
				Set<String> needRoles = entry.getValue();
				key = replaceRegix(key);
				boolean[] b = checkRole(key, url, needRoles, hasRoles);

				// 如果有一处匹配则把标记改为false
				if (b[1]) {
					matchRegFlag = false;
				}
				if (b[0]) {
					ret = true;
					break;
				}
			}
			// 如果当前url和规则中没有任何一处匹配则返回true
			if (matchRegFlag)
				ret = true;
		}
		return ret;
	}

	/**
	 * 检查权限
	 * 
	 * @param regix
	 *            正则表达式
	 * @param url
	 *            当前url
	 * @param needRoles
	 *            需要的权限列表
	 * @param hasRoles
	 *            拥有的权限列表
	 * @return ret[0]:true为拥有权限，false为没有权限；ret[1]：true为匹配成功，false为匹配不成功
	 */
	private static boolean[] checkRole(String regix, String url, Set<String> needRoles, Set<String> hasRoles) {
		boolean[] ret = { false, false };
		Pattern p = Pattern.compile(regix);
		Matcher m = p.matcher(url);
		if (m.matches()) {
			//添加到缓存
			Plum.getUrlRegixCacheMap().put(url, regix);
			ret[1] = true;
			if (hasRoles != null) {
				for (String rl : hasRoles) {
					if (needRoles.contains(rl)) {
						ret[0] = true;
						break;
					}
				}
			}
		}
		return ret;
	}

	/**
	 * 判断url是否允许未授权通过(allowMap)
	 * 
	 * @param url
	 * @return
	 */
	public static boolean isAllow(String url) {
		boolean ret = false;
		for (Map.Entry<String, String> entry : Plum.getAllowMap().entrySet()) {
			String key = entry.getKey();
			String val = entry.getValue();
			key = replaceRegix(key);
			Pattern p = Pattern.compile(key);
			Matcher m = p.matcher(url);
			if (m.matches() && Plum.PLUM_ALLOW.equals(val)) {
				ret = true;
				break;
			}
		}
		return ret;
	}
}
